#!/usr/bin/perl -w

BEGIN {
        if(!exists($::ENV{TRUECL_CODE})) {
                die("Missing TRUECL_CODE environment variable.");
        }
        if(!exists($::ENV{TRUECL_LOG})) {
                die("Missing TRUECL_LOG environment variable.");
        }
        if(!exists($::ENV{TRUECL_CFG})) {
                die("Missing TRUECL_CFG environment variable.");
        }
	push @::INC,"$::ENV{TRUECL_CODE}/lib";
}

use strict;
use Carp;
use Carp qw(cluck);
use Getopt::Long;
use cllogging;
use File::Basename;
use Sys::Hostname;
use Data::Dumper;
use cldb;
use clstatdb;
use clreq;
use Sys::Hostname;
use daemon_connect;
use clreqdxml;

$::_SPC=" " x length(basename($::0));
$::_PRG=basename($::0);
$::PKI=1;
eval "use Crypt::RSA;";
$::PKI=0 if $@;

sub logit {
my $type=shift;
my $cline;
my $xx;

	confess("No log object defined.") if !defined($::LOGGER);
	for $cline (@_) {
		my @F=split(/\n/,$cline);
		for $xx (@F) {
			$::LOGGER->msg($type,$xx);
		}
	}
}

sub msg {
	logit("msg",@_);
}

sub warning {
	logit("warn",@_);
}

sub error {
my $rc;

	if(scalar(@_)>1) {
		my $x=scalar(@_)-1;
		$rc=$_[$x];
		#########################################################
		# Treat the last as an exit code - if it is numeric.	#
		#########################################################
		if($rc =~ /^\d+$/) {
			@_=@_[0 .. $x-1];
		} else {
			undef $rc;
		}
	}
	logit("error",@_);
	exit($rc) if defined($rc);
}

$::ARG_force=0;
$::ARG_silent=0;
$::ARG_quiet=0;
$::ARG_verbose=0;
$::ARG_application=undef;
$::ARG_newnode=undef;
$::ARG_newstate=undef;
$::ARG_timeout=undef;
$::ARG_lwidth=undef;

sub usage {
	print STDERR "
Usage: $::_PRG --application A [--newnode N] [--newstate X]
       $::_SPC [--verbose|--quiet|--silent] [--force] [--timeout N]

Written by Simon Edwards, (C) 2006.
";
	exit(1);
}

if(!GetOptions(
	"F|force"			=> \$::ARG_force,
	"V|verbose"			=> \$::ARG_verbose,
	"S|silent"			=> \$::ARG_silent,
	"Q|quiet"			=> \$::ARG_quiet,
	"A|application=s"		=> \$::ARG_application,
	"N|newnode=s"			=> \$::ARG_newnode,
	"N|newstate=s"			=> \$::ARG_newstate,
	"T|timeout=i"			=> \$::ARG_timeout,
	"W|lwidth=i"			=> \$::ARG_lwidth,
	)) {
	usage;
}

if(defined($::ARG_lwidth)) {
	$::LOGGER=new cllogging("-",$::ARG_lwidth);
} else {
	$::LOGGER=new cllogging("-",80);
}

if(!defined($::LOGGER)) {
	print STDERR "Error: Unable to create logfile object.\n";
	exit(1);
}

#########################################################################
# Change the logging level based on command line arguments.		#
#########################################################################
$::LOGGER->set_silent if $::ARG_silent;
$::LOGGER->set_quiet if $::ARG_quiet;
$::LOGGER->set_verbose if $::ARG_verbose;

if(!defined($::ARG_application)) {
	usage;
}

#########################################################################
# First thing to do is to validate the node specified and the app	#
# are part of the cluster that this machine is part of.			#
#########################################################################

$::NODES=();
{
	my $CLDB=new cldb("$::ENV{TRUECL_CFG}/config.db");
	if(!defined($CLDB)) {
		error("Unable to connect to 'config' database.","Reason: " . (cldb::error)[1] , 1);
	}

	my $res=$CLDB->get_rows("SELECT value FROM globals WHERE var = 'clustername';");
	if(!defined($res->[0]->{value})) {
		error("Incomplete cluster definition in local database [no cluster name].",1);
	}
	my $cluster=$res->[0]->{value};
	$res=$CLDB->get_rows("SELECT * FROM nodes;");
	if(!defined($res->[0]->{name})) {
		error("Incomplete cluster definition in local database [no node details].",1);
	}
	for (@$res) {
		push @::NODES,$_->{name};
	}
	$CLDB->disconnect;
}

#########################################################################
# Check all the nodes looking for request daemons to send info to.	#
#########################################################################

msg("Checking for available Request Daemons - please wait.");
$::CLREQDXML=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
if(! defined($::CLREQDXML)) {
	error("Unable to load Request Daemon configuration info.", "Reason: " . clreqdxml::error(),1);
}

{
	$::REQD=new clreq(hosts => [@::NODES], clreqdxml => $::CLREQDXML, logger => $::LOGGER, timeout => 5);
	my $results=$::REQD->run(cmd => ["echo"]);
	@::OK=(); my @DEAD=();
	for (keys(%$results)) {
		if($results->{$_} =~ /Alive!/) {
			push @::OK,$_;
		} else {
			push @DEAD,$_;
		}
	}
	if(@DEAD) {
		warning("The following list of nodes can not be contacted:",join(",",@DEAD));
	}
	msg("Required Request Daemons [" . join(",",@::OK) . "] running.");
}

#########################################################################
# Now we build some SQL based on the arguments that have been passed	#
# on the command line.							#
#########################################################################

{
	my $ctime=time;
	if(!defined($::ARG_newnode) && !defined($::ARG_newstate)) {
		$::SQL="UPDATE app_status SET host = '', status = 'STOPPED', starttime = '0', updatetime = '$ctime' WHERE name = '$::ARG_application';";
	} elsif(!defined($::ARG_newnode) && defined($::ARG_newstate)) {
		$::SQL="UPDATE app_status SET status = '$::ARG_newstate', starttime = '$ctime', updatetime = '$ctime' WHERE name = '$::ARG_application';";
	} elsif(!defined($::ARG_newstate) && defined($::ARG_newnode)) {
		$::SQL="UPDATE app_status SET host = '$::ARG_newnode', starttime = '$ctime', updatetime = '$ctime' WHERE name = '$::ARG_application';";
	} else {
		$::SQL="UPDATE app_status SET host = '$::ARG_newnode', status = '$::ARG_newstate', starttime = '$ctime', updatetime = '$ctime' WHERE name = '$::ARG_application';";
	}
}
		
#########################################################################
# Push out the sql to all nodes...					#
#########################################################################

$::exit=0;
{
	$::REQD=new clreq(hosts => [@::OK], clreqdxml => $::CLREQDXML, logger => $::LOGGER, timeout => 5);
	my $results=$::REQD->run(cmd => ["perform_adhoc_sql","SQL",$::SQL,"DBTYPE","status"]);

	my $ck;
	for $ck (sort(keys %$results)) {
		if($results->{$ck} =~ /^(ERROR|FAIL)/) {
			warning("Failed to set results on '$ck':",$results->{$ck});
			$::exit=99;
		}
	}
}
exit($::exit);
